
/*
* 死锁的成因涉及四个必要条件:
* 1.互斥使用(锁的基本特性) 一个线程持有一把锁时,另一个线程也想要这把锁,就会阻塞等待
* 2.不可抢占(锁的基本特性) A线程拿到了锁,B线程也想要该锁,只能等待A线程释放,不能主动去抢
* 3.请求保持(代码结构) 一个线程尝试获取多把锁(线程获取了锁1,尝试获取锁2,不会释放锁1)(吃着碗里的,看着锅里的)
* 4.循环等待/环路等待(代码结构)  等待的依赖关系,形成环了
*
* 不幸的是 1和2 都是锁的基本特性 在代码中只要占了3和4就会形成锁了
*
* 避免死锁的关键就是破坏3,4
* 对于三来说: 避免写 "锁" 嵌套逻辑
* 对于4来说: 可以约定加锁顺序,就可以避免循环等待(比如说给锁一个编号,先拿小的编号的锁,再拿编号大的锁)
* */

/*
* 两把锁导致的死锁问题
*
* 扩展 N个线程 M把锁
* 如哲学家就餐问题
* */
public class Demo13 {
    public static void main(String[] args) throws InterruptedException {
        Object locker1 = new Object();
        Object locker2 = new Object();

        Thread t1 = new Thread(()->{
            synchronized (locker1){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName()+"得到了locker1,现在需要locker2");
                synchronized (locker2){
                    System.out.println(Thread.currentThread().getName()+"得到了locker2和locker1");
                }
            }
        });

        Thread t2 = new Thread(()->{
            synchronized (locker2){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName()+"得到了locker2,现在需要locker1");
                synchronized (locker1){
                    System.out.println(Thread.currentThread().getName()+"得到了locker2和locker1");
                }
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}
